/*
 * test.c
 *
 *  Created on: 17/09/2011
 *      Author: Cristian

*/
#include "../includes/praid.h"
#include "includes/todo_incluido.h"


/***********************************************************************
FUNCION: EncolarPedidos
PROCESO: PRAID
Autor: Cristian Rios
PRE: haber establecido una conexion valida con un nuevo PFS
POST: un nuevo pedido del PFS es encolado
DEVUELVE: 0
FUNCION PPAL: agregar pedidos del PFS para luego ser distribuido entre los discos.
**********************************************************************/
void *EncolarPedidos(void* arg){
	param *p;
	p = (struct param*) arg;

	header_mensaje hMsj;
	mensaje *msj;
	msj = NULL;

	int16_t ret, i, maxfd;
	fd_set selector;

	while (1){

	FD_ZERO(&selector);
	maxfd = (p->arrayC[ (p->conexiones) - 1].sockId) + 1; 	//asigno el socket maximo + 1

	//Le indico al select los sockets que debe escuchar:
	for (i=0; i < p->conexiones; i++)	FD_SET(p->arrayC[i].sockId,&selector);

	ret = select(maxfd, &selector, NULL, NULL, NULL);

	if (ret < 0)

	for (i=0; i < p->conexiones; i++){
		if (FD_ISSET(p->arrayC[i].sockId, &selector)) {

				//Recibo el pedido

				recv(p->arrayC[i].sockId,
					(char*)&hMsj, sizeof(header_mensaje),
					0);
				msj = (mensaje*)malloc(sizeof(header_mensaje)+hMsj.payloadLength);
				memcpy(msj, &hMsj, sizeof(header_mensaje));

				recv(p->arrayC[i].sockId,
						(char*)msj->payload, msj->h.payloadLength,
						0);

				//Encolo el pedido

				if (cola_vacia(p->arrayC[i].cola)){

						crear_cola(p->arrayC[i].cola, sizeof(mensaje));
						agregar_nodo(p->arrayC[i].cola, msj);
						p->arrayC[i].estado = OCUPADO;

					} else {
						agregar_nodo(p->arrayC[i].cola, msj);
					}
						free(msj);
			} else continue;
		}

	}
	return 0;
}

/***********************************************************************
FUNCION: AnalizarNuevosClientes
PROCESO: PRAID
Autor: Cristian Rios
PRE: haber recibido una cola de nuevosProcesos que no este vacia.
POST: se establece una nueva conexion con un nuevo proceso PFS o PPD para enviar y recibir paquetes NIPC, o se rechazan en caso de error.
DEVUELVE: 0
FUNCION PPAL: Se procesa si el handshake con el nuevo proceso PFS o PPD es correcto o no.
**********************************************************************/
void *AnalizarNuevosClientes(void *arg){

	struct param *p;
	p = (struct param *) arg;

	int8_t t_creado = 0; //falso

	uint8_t *sock;
	sock = (uint8_t *)(p->newProcess->salida->datos);


	while (1){

		if (p->newProcess == NULL) sleep (1); //cola vacia de nuevos procesos
		else {
			header_mensaje hMsj;
			mensaje *msj;
			msj = NULL;

			sock = p->newProcess->salida->datos;

			recv(*sock, (char*)&hMsj, sizeof(header_mensaje), 0);
			msj = (mensaje*)malloc(sizeof(header_mensaje)+hMsj.payloadLength);
			memcpy(msj, &hMsj, sizeof(header_mensaje));
			recv(*sock, (char*)msj->payload, msj->h.payloadLength, 0);

			if (msj->h.type == HANDSHAKE){
					if(msj->payload == NULL && (*p).numDiscos == 0){

						/*******Si es un PFS pero no hay discos conectados todavia, rechazo el pedido******/

						mensaje *msjRta;
						msjRta = NULL;
						strcpy(msjRta->payload, "Error, no hay ningun disco conectado aun.\n");
						msjRta->h.type = 0;
						msjRta->h.sector = 0;
						msjRta->h.payloadLength = sizeof(msjRta->payload);

						send(*sock, (char*)msjRta, sizeof(header_mensaje)+ msjRta->h.payloadLength, 0);


						close(*sock);

						//eliminarPrimerNodoProceso(&p);
						destruir_nodo(p->newProcess);

						free(msjRta);
						}

					else if (msj->payload == NULL && (*p).numDiscos != 0 ){

						/*********Acepto al proceso File System***********/

						mensaje *msjRta;
						msjRta = NULL;

						msjRta->h.type = 0;
						msjRta->h.sector = 0;
						msjRta->h.payloadLength = 0;
						msjRta->payload = NULL;

						send(*sock,
							(char*)msjRta,
							sizeof(header_mensaje)+ msjRta->h.payloadLength,
							0);

						struct pedidoPFS pedido;
						pedido.t_param = p;

						int8_t pos;
						pos = BuscarSocketLibre (p->arrayC, p->conexiones);

						p->arrayC[pos].estado = OCUPADO;
						p->arrayC[pos].cola = NULL;
						p->arrayC[pos].sockId = *sock;

						free(msjRta);
						//close(*(p->newProcess->sockId));

						//eliminarPrimerNodoProceso(&p);
						destruir_nodo(p->newProcess);

						if (t_creado == 0) {
						pthread_t t_new_cli;
						pthread_create(&t_new_cli, NULL, EncolarPedidos, &p);
						t_creado = 1;
						};

						}
					return 0;
				}
				else if (&msj->h.type == HANDSHAKE && &msj->payload != NULL){

					/*********Acepto al nuevo proceso Disco*******/

					mensaje *msjRta;
					msjRta = NULL;

					msjRta->h.type = 0;
					msjRta->h.sector = 0;
					msjRta->h.payloadLength = 0;
					msjRta->payload = NULL;

					send(*sock,
							(char*)msjRta,
							sizeof(header_mensaje)+ msjRta->h.payloadLength,
							0);

					struct infoDeDisco disc;
					disc.sockId = *sock;
					disc.idDisco = msj->payload;
					disc.estado = INACTIVO;
					disc.ultimoAcceso = 0;

					InsertarNodoDisco(disc, &(*p));
					((*p).numDiscos)++;

					free(msjRta);

					//eliminarPrimerNodoProceso(&p);  //Ya atendido
					destruir_nodo(p->newProcess);

					//Crear thread asociado al PPD aqui

				}
				else {

					/*********Rechazo al nuevo proceso disco**************/

					mensaje *msjRta;
					msjRta = NULL;

					msjRta->h.type = 0;
					msjRta->h.sector = 0;
					msjRta->h.payloadLength = 0;
					strcpy(msjRta->payload, "Error al procesar al nuevo proceso Disco\n");

					send(*sock, (char*)msjRta, sizeof(header_mensaje)+ msjRta->h.payloadLength, 0);

					close(*sock);

					destruir_nodo(p->newProcess);
					//eliminarPrimerNodoProceso(&p);
				}

		}

	}

	return 0;
}

/***********************************************************************
FUNCION: ListenNewProcesses
PROCESO: PRAID
Autor: Cristian Rios
PRE: haber creado e inicializado la lista de lstProc del tipo nuevoProceso
POST: un nuevo nodo es encolado en la lista de nuevos procesos para ser analizado su validez
DEVUELVE: 0
FUNCION PPAL: crea un nuevo puerto de escucha en donde llegaran nuevos handshake de PFS o PPD que seran encolados para luego ser analizados en otro thread.
**********************************************************************/
void *ListenNewProcesses(void *arg)
{

		param_escucha *new_p;
		new_p = (param_escucha*) arg;


		uint16_t iSock, new_sock_cli;
		uint8_t resultado;
		struct sockaddr_in addr;

		addr.sin_addr.s_addr=INADDR_ANY;
		addr.sin_family = AF_INET;
		addr.sin_port=htons(new_p->cfg.v_puerto);

		bzero ( &(addr.sin_zero), 8);

		iSock = socket ( AF_INET, SOCK_STREAM, 0);
		if (iSock == -1) printf("ERROR AL CREAR SOCKET");

		resultado = bind(iSock, (struct sockaddr*) &addr, sizeof(addr));
		if (resultado == -1) {
			printf("ERROR AL UNIR EL SOCKET");
			exit(1);
			}

		char *some_addr;
		some_addr = inet_ntoa (addr.sin_addr);
		printf(" Direccion IP del socket de escucha %d ", iSock);
		printf("es: %s ", some_addr);
		printf("y numero de puerto: %d\n", new_p->cfg.v_puerto);

		while(1){

		printf("entrando al while\n");

		listen(iSock, BACKLOG);

		socklen_t addrlen = sizeof (struct sockaddr_in);

		new_sock_cli = accept ( iSock, (struct sockaddr *)&addr, &addrlen);

		if (new_sock_cli == -1) {
			perror("accept");
			continue;
		}

		//InsertarNuevoNodoProceso (new_sock_cli, &(new_p->nuevo_p));

		if (cola_vacia(new_p->nuevo_p)){

				crear_cola(new_p->nuevo_p, sizeof(uint16_t));
				agregar_nodo(new_p->nuevo_p, &new_sock_cli);

				} else 	agregar_nodo(new_p->nuevo_p, &new_sock_cli);

			}
		return 0;
}

/***********************************************************************
FUNCION: BuscarSocketLibre
PROCESO: PRAID
Autor: Cristian Rios
PRE: haber establecido una conexion correcta con un nuevo PFS
POST: se encuentra una posicion disponible en el array de conexiones con el PFS
DEVUELVE: la posicion del array de conexiones que se encuentre libre.
FUNCION PPAL: buscar la posicion del array de conexiones en donde su estado sea LIBRE.
**********************************************************************/
int16_t BuscarSocketLibre (struct arrConexiones *arrayC, uint8_t cant){
	uint16_t i;
	uint8_t ENCONTRADO;
	ENCONTRADO = 0;	//falso

	while (ENCONTRADO == 0){
		for (i=0; i< cant; i++){
			if (arrayC[i].estado==LIBRE) {
				ENCONTRADO = 1;
				break;
			}
		}
		if (ENCONTRADO == 0) sleep (1);
				else break;
	}
	return i;
}

/***********************************************************************
FUNCION: AtenderPedidos
PROCESO: PRAID
Autor: Cristian Rios
PRE: que el PFS ya haya enviado pedidos de lectura/escritura a disco
POST: los pedidos del PFS quedan distribuidos entre todos los discos
DEVUELVE: void*
FUNCION PPAL: Realizar la distribucion de pedidos del PFS entre todos los discos
**********************************************************************/
void *AtenderPedidos(void *arg){
	return 0;
}
